package fid

import (
	"reflect"

	"github.com/sirupsen/logrus"
)

type Config struct {
	Host string `conf:"host,default(0.0.0.0)"`
	Port string `conf:"port,default(8080)"`
	Path string `conf:"path,default(/)"`
	// ControllerRoutePrefix string `conf:"route.prefix,omit"`

	SessionConfig SessionConfig `conf:"session,omit"`
}

func defaultConfig() Config {
	return Config{
		Host: "0.0.0.0",
		Port: "8080",
		Path: "/",
		// ControllerRoutePrefix: "ST",
	}
}

func mergeConfig(cfg Config, fromCfg Config) Config {
	mergeUnNilFields(&cfg, &fromCfg)
	return cfg
}

// to,from must be pointer
func mergeUnNilFields(to, from interface{}) {
	toTyp := reflect.TypeOf(to)
	toVal := reflect.ValueOf(to)
	fromTyp := reflect.TypeOf(from)
	fromVal := reflect.ValueOf(from)
	if toTyp.Kind() != reflect.Ptr || toTyp.Elem().Kind() != reflect.Struct ||
		fromTyp.Kind() != reflect.Ptr || fromTyp.Elem().Kind() != reflect.Struct {
		logrus.Errorf("mergeUnNilFields param unknown")
		return
	}
	toTyp = toTyp.Elem()
	toVal = toVal.Elem()
	fromTyp = fromTyp.Elem()
	fromVal = fromVal.Elem()

	fromValMap := make(map[string]reflect.Value)
	fromTypMap := make(map[string]reflect.Type)
	for i := 0; i < fromTyp.NumField(); i++ {
		iVal := fromVal.Field(i)
		iTyp := fromTyp.Field(i)
		if haveSetValue(iTyp.Type, iVal) {
			fromValMap[iTyp.Name] = iVal
			fromTypMap[iTyp.Name] = iTyp.Type
		}
	}
	for i := 0; i < toTyp.NumField(); i++ {
		iVal := toVal.Field(i)
		iTyp := toTyp.Field(i)
		if !iVal.CanSet() {
			continue
		}

		if fv, has := fromValMap[iTyp.Name]; has {
			iFromType := fromTypMap[iTyp.Name]
			if iTyp.Type != iFromType {
				continue
			}
			iVal.Set(fv)
		}
	}
}

func haveSetValue(typ reflect.Type, val reflect.Value) bool {
	switch typ.Kind() {
	case reflect.String:
		return len(val.String()) > 0
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		return val.Int() != 0
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
		return val.Uint() != 0
	case reflect.Float32, reflect.Float64:
		return val.Float() != 0
	}
	// unsupport
	return true
}
